﻿//----------------------------------------------------------------
//CodeArt (2011)
//
//Author: jianyi0115@163.com
//Created: 2011-6-20
//
//History:
// 
//----------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint.WebPartPages;
using Microsoft.SharePoint;
using System.Xml;
using System.Reflection;
using System.Web.UI.WebControls.WebParts;
using WebPart = System.Web.UI.WebControls.WebParts.WebPart;
using Microsoft.SharePoint.Administration;

namespace CodeArt.SharePoint.SmartQuery
{
    public abstract class ListWebPartQueryManager
    {
        public abstract void ChangeQuerySchema(string query);
        public abstract void RestoreQuerySchema();
        public abstract SPView GetQueryView();

        public virtual void ResetPaging()
        {
        }
                
        public static void ChangeWebPartsQuery(SPList curList, WebPartCollection parts, string qxml, bool resetPaging)
        {
            if (String.IsNullOrEmpty(qxml)) return;

            string listId = "{" + curList.ID.ToString() + "}";

            foreach (WebPart wp in parts)
            {
                if (wp.Visible == false || wp.Hidden)
                    continue;

                if (ChangeWebPartQuery(curList, wp, qxml, resetPaging))
                {
                    break;
                }
            }
        }

        public static SPView GetQueryView(SPList curList, WebPartCollection parts)
        {
            string listId = "{" + curList.ID.ToString() + "}";

            foreach (WebPart wp in parts)
            {
                if (wp.Visible == false || wp.Hidden)
                    continue;

                var typeName = wp.GetType().Name;

                if (typeName == "ListViewWebPart")
                {
                    Microsoft.SharePoint.WebPartPages.ListViewWebPart listWp = (Microsoft.SharePoint.WebPartPages.ListViewWebPart)wp;
                    if (String.Compare(listWp.ListName, listId, true) != 0)
                    {
                        continue;
                    }
                    var queryManager = new ListViewWebPartQueryManager(listWp, curList.Views[new Guid(listWp.ViewGuid)]);
                    return queryManager.GetQueryView();
                }
                else if (typeName == "XsltListViewWebPart")
                {
                    var queryManager = GetXsltListViewWebPartQueryManager(wp, curList);
                    return queryManager.GetQueryView();
                }
            }
            return null;
        }

        public static bool ChangeWebPartQuery(SPList curList, WebPart wp, string qxml,bool resetPaging)
        {
            if (String.IsNullOrEmpty(qxml)) return false;

            string listId = "{" + curList.ID.ToString() + "}";                        
            var typeName = wp.GetType().Name;

            if (typeName == "ListViewWebPart")
            {
                Microsoft.SharePoint.WebPartPages.ListViewWebPart listWp = (Microsoft.SharePoint.WebPartPages.ListViewWebPart)wp;
                if (String.Compare(listWp.ListName, listId, true) != 0)
                {
                    return false;
                }
                var queryManager = new ListViewWebPartQueryManager(listWp, curList.Views[new Guid(listWp.ViewGuid)]);
                if (String.IsNullOrEmpty(qxml))
                {
                    queryManager.RestoreQuerySchema();
                }
                else
                {
                    queryManager.ChangeQuerySchema(qxml);
                }
                return true;
            }
            else if (typeName == "XsltListViewWebPart")
            {
                //var queryManager = new XsltListViewWebPartQueryManager(wp, curList);
                var queryManager = GetXsltListViewWebPartQueryManager(wp, curList);
                if (String.IsNullOrEmpty(qxml))
                {
                    queryManager.RestoreQuerySchema();
                }
                else
                {
                    queryManager.ChangeQuerySchema(qxml);
                    if (resetPaging)
                    {
                        queryManager.ResetPaging();
                    }
                }
                return true;
            }
            else
            {
                return false;
            }
        }

        static int? serverVersion;

        static ListWebPartQueryManager GetXsltListViewWebPartQueryManager(WebPart wp,SPList curList)
        {
            if (serverVersion == null)
            {
                serverVersion = SPFarm.Local.BuildVersion.Major;
            }
            if (serverVersion.Value > 14) //for sp2013
            {
                return new XsltListViewWebPartQueryManager2013(wp, curList);
            }
            else 
            {
                return new XsltListViewWebPartQueryManager2010(wp, curList);
            }
        }

        protected void ChangeSchemaXmlQuery(XmlDocument doc, string query)
        {
            if (String.IsNullOrEmpty(query)) return;

            string innerQuery = GetInnerWhere(query);
            if (innerQuery == "") return;

            XmlNode queryNode = doc.DocumentElement.SelectSingleNode("Query");
            //queryNode.InnerXml = qxml;

            XmlNode oldWhereNode = queryNode.SelectSingleNode("Where");

            if (oldWhereNode != null)
                queryNode.RemoveChild(oldWhereNode);

            XmlNode newWhereNode = doc.CreateElement("Where");

            if (oldWhereNode != null) //确保视图上固定的过滤条件始终起作用
            {
                newWhereNode.InnerXml = "<And>" + oldWhereNode.InnerXml + innerQuery + "</And>";
            }
            else
            {
                newWhereNode.InnerXml = innerQuery;
            }

            queryNode.AppendChild(newWhereNode);

            XmlNode viewEmptyNode = doc.DocumentElement.SelectSingleNode("ViewEmpty");
            if (viewEmptyNode != null)
            {
                viewEmptyNode.InnerXml = "<HTML><![CDATA[<font color='red'><b>" +
                ResourcesHelper.GetResourceString("NoFoundItems", "未找到符合查询条件的记录。") + "</b></font>]]></HTML>";
            }
        }

        public static string MergeViewQuery(SPView view, string where2)
        {
            if (String.IsNullOrEmpty(view.Query)) 
            {
                return where2;
            }
            if (String.IsNullOrEmpty(where2))
            {
                return view.Query;
            }

            string orderBy = "" ;
            var innerQuery1 = GetInnerWhere(view.Query,out orderBy);
            if (String.IsNullOrEmpty(innerQuery1))
            {
                return where2 + orderBy;
            }
           
            var innerQuery2 = GetInnerWhere(where2);
            if (String.IsNullOrEmpty(innerQuery2))
                return view.Query;
            else
            {
                return "<Where><And>" + innerQuery1 + innerQuery2 + "</And></Where>" + orderBy;
            }
        }

        internal static string GetInnerWhere(string q, out string orderBy)
        {
            orderBy = null;
            if (String.IsNullOrEmpty(q))
                return null;

            XmlDocument doc = new XmlDocument();
            doc.LoadXml("<Query>" + q + "</Query>");
            var orderByNode = doc.DocumentElement.SelectSingleNode("OrderBy");
            if (orderByNode != null)
                orderBy = orderByNode.OuterXml;

            var whereNode = doc.DocumentElement.SelectSingleNode("Where");
            if (whereNode == null)
                return null;
            else
                return whereNode.InnerXml;
            //return doc.DocumentElement.InnerXml;
        }
        
        internal static string GetInnerWhere(string q)
        {
            if (String.IsNullOrEmpty(q))
                return null;

            XmlDocument doc = new XmlDocument();
            doc.LoadXml("<Query>"+q+"</Query>");
            var whereNode = doc.DocumentElement.SelectSingleNode("Where");
            if (whereNode == null)
                return null;
            else
                return whereNode.InnerXml;
            //return doc.DocumentElement.InnerXml;
        }
    }

    public class ListViewWebPartQueryManager : ListWebPartQueryManager
    {
        private ListViewWebPart webpart;
        private SPView view;
        public ListViewWebPartQueryManager(ListViewWebPart wp, SPView view)
        {
            this.webpart = wp;
            this.view = view;
        }
        public override void ChangeQuerySchema(string query)
        {
            XmlDocument doc = new XmlDocument();
            doc.LoadXml(this.webpart.ListViewXml);
            ChangeSchemaXmlQuery(doc, query);
            this.webpart.ListViewXml = doc.InnerXml;
        }
        public override void RestoreQuerySchema()
        {
            webpart.ListViewXml = this.view.HtmlSchemaXml; //还
        }
        public override SPView GetQueryView()
        {
            return this.view;
        }
    }
    
    /// <summary>
    /// for 2010 XsltListViewWebPart
    /// 为了兼容07，用反射调用。
    /// </summary>
    class XsltListViewWebPartQueryManager : ListWebPartQueryManager
    {
        private WebPart webpart;
        private SPView view;
        private SPList list;
        private PropertyInfo xmlDefinitionProperty;
        private PropertyInfo viewGuidProperty;
        public XsltListViewWebPartQueryManager(WebPart wp, SPList list)
        {            
            this.webpart = wp;
            this.list = list;
            xmlDefinitionProperty = wp.GetType().GetProperty("XmlDefinition");
            viewGuidProperty = wp.GetType().GetProperty("ViewGuid");
            this.view = list.Views[new Guid(viewGuidProperty.GetValue(wp,null).ToString())];
        }
        public override void ChangeQuerySchema(string query)
        {
            XmlDocument doc = new XmlDocument();
            doc.LoadXml(xmlDefinitionProperty.GetValue(this.webpart, null).ToString());
            ChangeSchemaXmlQuery(doc, query);
            this.xmlDefinitionProperty.SetValue(this.webpart, doc.InnerXml, null);            
        }
        public override void RestoreQuerySchema()
        {
            this.xmlDefinitionProperty.SetValue(this.webpart,  this.view.HtmlSchemaXml, null);
        }
        public override SPView GetQueryView()
        {
            return this.view;
        }
    }

    public class XsltListViewWebPartQueryManager2010 : ListWebPartQueryManager
    {
        protected XsltListViewWebPart webpart;
        //private SPView view;
        protected SPList list;

        public XsltListViewWebPartQueryManager2010(WebPart wp, SPList list)
        {
            this.webpart = (XsltListViewWebPart)wp;
            this.list = list;
            //this.view = list.Views[new Guid(this.webpart.ViewGuid)];
        }

        public override void ResetPaging()
        {
            webpart.ParameterValues.Set("dvt_firstrow", "1");
            webpart.ParameterValues.Collection.Remove("dvt_nextpagedata");
            webpart.ParameterValues.Collection.Remove("dvt_prevpagedata");
            webpart.ParameterValues.Collection.Remove("dvt_startposition");
        }

        public override void ChangeQuerySchema(string query)
        {
            XmlDocument doc = new XmlDocument();
            doc.LoadXml(this.webpart.XmlDefinition);
            ChangeSchemaXmlQuery(doc, query);
            this.webpart.XmlDefinition = doc.InnerXml;
            //this.webpart.AsyncRefresh = true;  强制设置AsyncRefresh会造成字段过滤失效， 必须手工设置
        }

        public override void RestoreQuerySchema()
        {
        }

        public override SPView GetQueryView()
        {
            if (!String.IsNullOrEmpty(this.webpart.ViewGuid))
                return list.Views[new Guid(this.webpart.ViewGuid)];
            else
            {
                if (String.IsNullOrEmpty(this.webpart.XmlDefinition))
                    throw new Exception("can't find XmlDefinition of listView");
                 XmlDocument doc = new XmlDocument();
                 doc.LoadXml(this.webpart.XmlDefinition);

                 return new SPView(this.list, doc);
            }
        }
    }

    public class XsltListViewWebPartQueryManager2013 : XsltListViewWebPartQueryManager2010
    {
        public XsltListViewWebPartQueryManager2013(WebPart wp, SPList list):base(wp,list)
        {          
        }

        public override void ResetPaging()
        {
            webpart.ParameterValues.Set("dvt_firstrow", "1");
            webpart.ParameterValues.Collection.Remove("dvt_nextpagedata");
            webpart.ParameterValues.Collection.Remove("dvt_prevpagedata");
            webpart.ParameterValues.Collection.Remove("dvt_startposition");
        }

        public override void ChangeQuerySchema(string query)
        {
            //this.webpart.ServerRender = true;

            XmlDocument doc = new XmlDocument();
            doc.LoadXml(this.webpart.XmlDefinition);
            ChangeSchemaXmlQuery(doc, query);
            this.webpart.XmlDefinition = doc.InnerXml;

            //this.webpart.View.Query = doc.DocumentElement.SelectSingleNode("Query").InnerXml;

             //不支持客户端呈现模式，故强制设置为服务段呈现，即SP2010模式
            //客户端呈现模式下，服务端直接按照视图的查询条件生成json: XsltListViewWebPart.RenderWebPart:
            /*writer2.Write(this.ClientRenderListDataVar);
            writer2.Write(" = ");
            if (!this.SPList.HasExternalDataSource || ((this.SPList.HasExternalDataSource && (this.Page != null)) && this.Page.IsCallback))
            {
                this.SPList.RenderListData(this, base.view, writer2);
            }
            else
            {
                writer2.Write("{ \"Row\" : [] }");
            } */


           // var doc2 = new XmlDocument();
           // doc2.LoadXml(this.view.HtmlSchemaXml);
           // ChangeSchemaXmlQuery(doc, query);
            //this.view.HtmlSchemaXml = doc.InnerXml;

           // this.view.Query = query;

            //MethodInfo methodEnsureNecessaryFields = this.webpart.GetType().GetMethod("EnsureNecessaryFields", BindingFlags.Instance | BindingFlags.NonPublic);
            //methodEnsureNecessaryFields.Invoke(this.webpart, null);
        }
        public override void RestoreQuerySchema()
        {
            //this.xmlDefinitionProperty.SetValue(this.webpart, this.view.HtmlSchemaXml, null);
        }
    }
}
